
var DIR = cc.Enum({
    UP : 0,
    RIGHT : 1,
    DOWN : 2,
    LEFT : 3
});

//var Grid = cc.Class({
   
class Grid 
{
    public x = 0;
    public y = 0;
    public f = 0;
    public g = 0;
    public h = 0;
    public parent = null;
    /** -1障碍物， 0正常， 1起点， 2目的点 */
    public type = 0; 

    public constructor()
    {
	}
}

const {ccclass, property} = cc._decorator;

@ccclass
export default class GameMap extends cc.Component {

    @property(cc.Node)
    private player: cc.Node = null;

    @property(cc.TiledMap)
    private map:cc.TiledMap = null;

    @property(cc.TiledLayer)
    private hideLayer:cc.TiledLayer = null;

    @property(cc.TiledLayer)
    private mainLayer:cc.TiledLayer = null;

    @property(cc.Graphics)
    private lineNode:cc.Graphics = null;
    
    playerTile: any;
    playerDir: any;

    // LIFE-CYCLE CALLBACKS:

    onLoad () {
        //获取对象层
        var objects = this.map.getObjectGroup('objects');
        //获取对象
        var playerObj = objects.getObject('player');
        //获取坐标
        var playerPos = cc.v2(playerObj.x,playerObj.y);

        //设置玩家和敌人瓦片坐标
        this.playerTile = this.getTilePos(playerPos);
        console.warn("tilePos",this.playerTile,playerPos);
 
        //更新玩家位置
        this.updatePlayerPos();
        this.playerDir = DIR.DOWN;

        this.initMap();
    }

    private updatePlayerPos()
    {
        var pos = this.mainLayer.getPositionAt(this.playerTile);
        this.player.setPosition(pos);
        console.warn("玩家位置",this.player.getPosition(),this.playerTile);
    }

    //将地图中的像素单位坐标转化为瓦片单位坐标
    private getTilePos(posInPixel)
    {
        var mapSize = this.node.getContentSize();
        var tileSize = this.map.getTileSize();
        var x = Math.floor(posInPixel.x / tileSize.width);
        var y = Math.floor((mapSize.height - posInPixel.y) / tileSize.height);
        return cc.v2(x, y);
    }

    start () {
        // 初始化键盘输入监听
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
        this.node.on(cc.Node.EventType.TOUCH_END,this.clickMap,this);
    }

    // update (dt) {}
    onDestroy () 
    {
        // 取消键盘输入监听
        cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
    }

    private onKeyDown (event) 
    {
        // set a flag when key pressed
        switch(event.keyCode) {
            case cc.macro.KEY.up:
                this.playerDir = DIR.UP;
                var playerTarTile = cc.v2(this.playerTile.x, this.playerTile.y - 1);
                this.tryMoveToTarTile(playerTarTile);
                break;
            case cc.macro.KEY.down:
                this.playerDir = DIR.DOWN;
                var playerTarTile = cc.v2(this.playerTile.x, this.playerTile.y + 1);
                this.tryMoveToTarTile(playerTarTile);
                break;
            case cc.macro.KEY.left:
                this.playerDir = DIR.LEFT;
                var playerTarTile = cc.v2(this.playerTile.x - 1, this.playerTile.y);
                this.tryMoveToTarTile(playerTarTile);
                break;
            case cc.macro.KEY.right:
                this.playerDir = DIR.RIGHT;
                var playerTarTile = cc.v2(this.playerTile.x + 1, this.playerTile.y);
                this.tryMoveToTarTile(playerTarTile);
                break;
        }
    }

    private tryMoveToTarTile(newTile)
    {
        //检测mainLayer是否可以通过
        if (this.mainLayer.getTileGIDAt(newTile)) {
            cc.log('禁止通行!');
            return false;
        }
        this.playerTile = newTile;
        this.updatePlayerPos();
    }

    private _gridW = 32;   // 单元格子宽度
    private _gridH = 32;   // 单元格子高度
    private mapH = 19;     // 纵向格子数量
    private mapW = 19;     // 横向格子数量
    private is8dir = true; // 是否8方向寻路
   
    private path = [];
    private gridsList:Array<any> = null;

    private initMap()
    {    
        //初始化格子二维数组
        this.gridsList = null;
        this.gridsList = new Array(this.mapW + 1);
        for (let col=0;col<this.gridsList.length; col++)
        {
            this.gridsList[col] = new Array(this.mapH + 1);
        }
        
        //绘制19*19格子图片
        for (let i =0; i < this.mapW; i++)
        {
            for (let p=0; p < this.mapH; p++)
            {
                this.addGrid(i, p);
            }
        }

        this.path.length = 0;
        this.setpArr.length = 0
        this.index = 0;
        this.lineNode.clear();
    }

    //添加格子数据
    private addGrid(x, y)
    {
        let grid = new Grid();
        grid.x = x;
        grid.y = y;

        let tile = cc.v2(x,y)
        //检测mainLayer是否可以通过
        if (this.mainLayer.getTileGIDAt(tile))
        {
            //cc.log('禁止通行!',tile);
            grid.type = -1;
        }
        else{
            grid.type = 0;
        }
       
        this.gridsList[x][y] = grid;
    }

    private clickMap(e):void
    {       
        this.initMap();
        //设置起点
        let startX = this.playerTile.x;
        let startY = this.playerTile.y;
        let startPos = cc.v2(startX,startY);
        this.gridsList[startX][startY].type = 1;
        this.draw(startX, startY, cc.Color.YELLOW);

        //设置终点
        let playV = cc.v2(Math.round(e.getLocation().x),Math.round(e.getLocation().y))
        let playVAr =  this.node.convertToNodeSpaceAR(playV);
        let targetPos = this.getTilePos(playVAr);
        let endX = targetPos.x;
        let endY = targetPos.y;
        this.draw(endX, endY, cc.Color.BLUE);
        this.gridsList[endX][endY].type = 2;

        console.warn("起始点",startPos,targetPos)
        // 开始寻路
        this.findPath(startPos,targetPos);
    }
       

    private setpArr:Array<any> = new Array<any>();
    private index:number = 0;
    //画寻路图
    private draw(x, y, color:any = null)
    {
        color = color != undefined ? color : cc.Color.GRAY;
        this.lineNode.fillColor = color;
        this.lineNode.node.opacity = 120;
        let posV = cc.v2(x,y)
        let pos = this.mainLayer.getPositionAt(posV);
        //this.player.setPosition(pos);
        //this.playerTile = pos;
        //this.lineNode.fillRect(pos.x,pos.y,this._gridW,this._gridH);
        this.index++;
        if(this.index > 2)
        {
            this.setpArr.push(pos);
        }
    }
   
    private roleTweenFunc():void
    {   
        this.setpArr.reverse();
        for(let i=0; i < this.setpArr.length; i++)
        {
            this.scheduleOnce(()=>{
                this.player.x = this.setpArr[i].x;
                this.player.y = this.setpArr[i].y;
                //this.playerTile = this.setpArr[i];
                cc.warn(this.setpArr[i]);
            }, 0.5*i)

            if(i != this.setpArr.length - 1)
            {
                this.scheduleOnce(()=>{
                    this.lineNode.fillRect(this.setpArr[i].x,this.setpArr[i].y,this._gridW,this._gridH);
                }, 0.5 + 0.5*i)
            }
        }
    }

    private findPath(startPos, endPos)
    {
        let startGrid = this.gridsList[startPos.x][startPos.y];
        let endGrid = this.gridsList[endPos.x][endPos.y];

        var closeList = [];
        var openList = []
        var curGrid:any = null;
       
        openList.push(startGrid);
        curGrid = openList[0];
        
        while (openList.length > 0 && curGrid.type != 2){
            // 每次都取出f值最小的节点进行查找
            curGrid = openList[0];
            if (curGrid.type == 2){
                cc.log("找到终点",curGrid);
                this.generatePath(curGrid);
                return;
            }

            for(let i=-1; i<=1; i++){
                for(let j=-1; j<=1; j++){
                    if (i !=0 || j != 0){
                        let col = curGrid.x + i;
                        let row = curGrid.y + j;
                        if (col >= 0 && row >= 0 && col <= this.mapW && row <= this.mapH && this.gridsList[col][row].type != -1 && closeList.indexOf(this.gridsList[col][row]) < 0)
                        {
                            if (this.is8dir)
                            {
                                // 8方向 斜向走动时要考虑相邻的是不是障碍物
                                if (this.gridsList[col-i][row].type == -1 || this.gridsList[col][row-j].type == -1)
                                {
                                    continue;
                                }
                            } 
                            else {
                                // 四方形行走
                                if (Math.abs(i) == Math.abs(j)){
                                    continue;
                                }
                            }

                            // 计算g值
                            let g = curGrid.g +Math.floor(Math.sqrt(Math.pow(i*10,2) + Math.pow(j*10,2)));
                            if (this.gridsList[col][row].g == 0 || this.gridsList[col][row].g > g)
                            {
                                this.gridsList[col][row].g = g;
                                // 更新父节点
                                this.gridsList[col][row].parent = curGrid;
                            }
                            // 计算h值 manhattan估算法
                            this.gridsList[col][row].h = Math.abs(endPos.x - col) + Math.abs(endPos.y - row);
                            // 更新f值
                            this.gridsList[col][row].f = this.gridsList[col][row].g + this.gridsList[col][row].h;
                            // 如果不在开放列表里则添加到开放列表里
                            if (openList.indexOf(this.gridsList[col][row]) < 0)
                            {
                                openList.push(this.gridsList[col][row]);
                            }
                            // // 重新按照f值排序（升序排列)
                            // this.openList.sort(this._sortFunc);
                        }
                    }
                }
            }
            // 遍历完四周节点后把当前节点加入关闭列表
            closeList.push(curGrid);
            // 从开放列表把当前节点移除
            openList.splice(openList.indexOf(curGrid), 1);
            if (openList.length <= 0)
            {
                cc.log("寻路失败，没有路径可抵达");
            }

            // 重新按照f值排序（升序排列)
            openList.sort(this._sortFunc);
        }
    }

    private _sortFunc(x, y){
        return x.f - y.f;
    }

    private generatePath(grid)
    {   
        console.warn(grid);
        this.path.push(grid);
        while (grid.parent)
        {
            grid = grid.parent;
            this.path.push(grid);
        }
        cc.warn("寻路步数: " + this.path.length);
        for (let i=0; i<this.path.length; i++)
        {
            // 起点终点不覆盖，方便看效果
            //if (i!=0 && i!= this.path.length-1)
            if (this.path.length-1)
            {
                let grid = this.path[i];
                this.draw(grid.x, grid.y, cc.Color.GREEN);
            }
            if(i == this.path.length -1)
            {
                //结束了绘制任务行走动画
                this.roleTweenFunc();
            }
        }
    }

}
